package com.geeyuu.android.service;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.util.ByteArrayBuffer;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.os.Message;

import com.geeyuu.android.GeeConfig;
import com.geeyuu.android.GeePrefrence;
import com.geeyuu.android.utils.AndroidUtil;
import com.geeyuu.android.utils.CommonUtil;
import com.geeyuu.android.utils.LogUtil;
import com.geeyuu.android.utils.ProtocolUtil;

public class IMService extends BaseService {

    private static final String IP = "122.49.31.244";
    private static final int PORT = 6100;

    private Socket mSynSocket;
    private SendThread mSendThread;
    private RecvThread mRecvThread;

    private static int ramdomInteger = 0;
    private Random random;

    private OutputStream outputStream = null;
    private InputStream inputStream = null;

    private static boolean isNetWorkConnected = true;
    private static final boolean IS_NEED_THREAD_RUN = true;
    private RECmd loginReCmd;

    private ByteArrayBuffer recvBuffer;

    private ConcurrentHashMap<Integer, RECmd> mAryMsgSend;

    private static final long SEND_SLEEP_TIME_SLOW = Long.MAX_VALUE;

    private static final int REC_PACKAGE_MAX_LENGTH = 10240;
    private static final int REC_PACKAGE_MIN_LENGTH = 12;
    
    private boolean isGettingConnect = false;
    private Boolean lock = Boolean.TRUE;
    private boolean isRealConnected = Boolean.valueOf(false);
    
    private class SendThread extends Thread {
        
        public boolean isThreadStarted = false;
        
        @Override
        public synchronized void start() {
            isThreadStarted = true;
            super.start();
        }

        @Override
        public void run() {

            while (IS_NEED_THREAD_RUN) {
                if (!isNetWorkConnected) {
                    try {
                        Thread.sleep(SEND_SLEEP_TIME_SLOW);
                    } catch (InterruptedException e) {
                    }
                }
                if (!isRealConnected) {
                    if (!isGettingConnect) {
                        synchronized (lock) {
                            if (!isRealConnected) {
                                isGettingConnect = true;
                                initTacticsConnectedSocket();
                                if (null == mSynSocket || !mSynSocket.isConnected() || mSynSocket.isClosed()
                                        || mSynSocket.isOutputShutdown() || outputStream == null) {
                                    LogUtil.w(TAG, "已经尽力去获取连接了，还是失败了，继续睡眠");
                                    try {
                                        Thread.sleep(2000);
                                    } catch (InterruptedException e) {
                                    }
                                    isGettingConnect = false;
                                    continue;
                                }
                                isGettingConnect = false;
                            }
                        }
                    } else {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        continue;
                    }
                }

                try {
                    RECmd recCmd = null;
                    if (AndroidUtil.isSessionTimeOut(IMService.this)) {
                        recCmd = mAryMsgSend.get(RECmd.RELOGIN_SEQUENCE_ID);
                        if(null != recCmd){
                            if(recCmd.isServerException){
                                closeSocket();
                                recCmd.isServerException = false;
                            }else{
                                flush(recCmd);
                            }
                        }else{
                            LogUtil.w(TAG, "队列里的重登包为null");
                        }
                    } else {
                        Set<Integer> keys = mAryMsgSend.keySet();
                        if (null != keys) {
                            for (Integer key : keys) {
                                recCmd = mAryMsgSend.get(key);
                                flush(recCmd);
                            }
                        }
                    }
                } catch (Exception e) {
                    LogUtil.w(TAG, "发送消息有异常", e);
                    closeSocket();
                }

                try {
                    if(GeeConfig.isPrintLog){
                        LogUtil.v(TAG, "发送队列长度：" + mAryMsgSend.size());
                    }
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                }

            } // while
        } // run

        private void flush(RECmd recCmd) throws Exception {
            if (null != recCmd) {
                byte[] buffer = recCmd.getSendBuffer(IMService.this, mAryMsgSend);
                if (null != buffer && buffer.length > 0) {
                    if (null != outputStream) {
                        outputStream.write(buffer);
                        outputStream.flush();
                        LogUtil.i(TAG, "消息已经发送出去");
                    } else {
                        LogUtil.e(TAG, "socket输出流为null");
                    }
                }
            } else {
                LogUtil.w(TAG, "队列中的recCmd为null");
            }
        }

    } // Runnable

    private class RecvThread extends Thread {
        
        public boolean isThreadStarted = false;
        
        @Override
        public synchronized void start() {
            isThreadStarted = true;
            super.start();
        }
        
        @Override
        public void run() {

            while (IS_NEED_THREAD_RUN) {
                if (!isNetWorkConnected) {
                    try {
                        Thread.sleep(SEND_SLEEP_TIME_SLOW);
                    } catch (InterruptedException e) {
                    }
                }
                if (!isRealConnected) {
                    if (!isGettingConnect) {
                        synchronized (lock) {
                            if (!isRealConnected) {
                                isGettingConnect = true;
                                initTacticsConnectedSocket();
                                if (null == mSynSocket || !mSynSocket.isConnected() || mSynSocket.isClosed()
                                        || mSynSocket.isInputShutdown() || inputStream == null) {
                                    LogUtil.w(TAG, "已经尽力去获取连接了，还是失败了，继续睡眠");
                                    try {
                                        Thread.sleep(2000);
                                    } catch (InterruptedException e) {
                                    }
                                    isGettingConnect = false;
                                    continue;
                                }
                                isGettingConnect = false;
                            }
                        }
                    } else {
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        continue;
                    }
                }

                int bufferLength = 0;
                try {
                    bufferLength = inputStream.available();
                } catch (Exception e) {
                    LogUtil.w(TAG, "获取输入流长度失败", e);
                    closeSocket();
                }
                if (bufferLength == 0) {
                    LogUtil.v(TAG, "服务端没有来消息，睡眠");
                    if(mAryMsgSend.size() > 2){
                        try {
                            Thread.sleep(500);
                        } catch (InterruptedException e) {
                        }
                    }else{
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                        }
                    }
                    continue;
                }
                LogUtil.i(TAG, "服务器来消息了，输入流长度length:" + bufferLength);

                byte[] buffer = new byte[bufferLength];

                try {
                    if (null != inputStream) {
                        inputStream.read(buffer, 0, bufferLength);
                    }
                } catch (Exception e) {
                    LogUtil.w(TAG, "读取输入流到buffer有异常", e);
                    closeSocket();
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ex) {
                    }
                    continue;
                }

                if (recvBuffer.length() > 0) {
//                    if(bufferLength >= nextPackageMinLength){
                        LogUtil.w(TAG, "下半截来了，开始组装");
                        nextPackageMinLength = 0;
                        recvBuffer.append(buffer, 0, bufferLength);
                        byte[] allBuffer = recvBuffer.toByteArray();
                        recvBuffer.clear();
                        LogUtil.w(TAG, "开始解析组装完成的包");
                        decode(allBuffer);
//                    }else{
//                        decode(buffer);
//                    }
                } else {
                    decode(buffer);
                }

            } // while

        }

        private int nextPackageMinLength = 0;
        
        private void decode(final byte[] buffer) {
            int bufferLength = buffer.length;
            if (bufferLength >= REC_PACKAGE_MIN_LENGTH && bufferLength <= REC_PACKAGE_MAX_LENGTH) {
                int packageLength = ProtocolUtil.byteArrayToUShort(buffer, 0);
                LogUtil.v(TAG, "解析得到包的长度为:" + packageLength);
                if (packageLength >= REC_PACKAGE_MIN_LENGTH && packageLength <= REC_PACKAGE_MAX_LENGTH) {
                    int command = ProtocolUtil.byteArrayToUShort(buffer, 2);
                    LogUtil.v(TAG, "解析得到命令:" + command);
                    if (command > ProtocolCommand.MIN_COMMAND && command < ProtocolCommand.MAX_COMMAND) {
                        int protocolVersion = ProtocolUtil.byteArrayToUShort(buffer, 4);
                        LogUtil.v(TAG, "解析得到协议版本号:" + protocolVersion);
//                        if (protocolVersion == ProtocolCommand.PROTOCOL_VERSION) {
                            int sequenceID = ProtocolUtil.byteArrayToUShort(buffer, 6);
                            LogUtil.v(TAG, "解析得到流水号:" + sequenceID);
                            if (mAryMsgSend.containsKey(sequenceID) || ProtocolCommand.isPushCommand(command)) {
                                long errorCode = ProtocolUtil.byteArrayToUInt(buffer, 8);
                                LogUtil.v(TAG, "解析得到错误号:" + errorCode);
                                if (errorCode >= ProtocolCommand.MIN_ERROR_CMD && errorCode <= ProtocolCommand.MAX_ERROR_CMD) {
                                    // 进到这里来，说明这个包是正确的
                                    LogUtil.i(TAG, "接收包验证全部通过!");
                                    if (packageLength == bufferLength) { // 单段消息
                                        String jsonStr = decodeJsonBytes(packageLength, buffer);
                                        sendMsgToActivity(command, protocolVersion, sequenceID, errorCode, jsonStr);
                                    } else if (packageLength < bufferLength) { // 多段消息
                                        String jsonStr = decodeJsonBytes(packageLength, buffer);
                                        sendMsgToActivity(command, protocolVersion, sequenceID, errorCode, jsonStr);
                                        LogUtil.d(TAG, "开始处理多段消息");
                                        final int remainLength = bufferLength - packageLength;
                                        final byte[] remainBytes = new byte[remainLength];
                                        System.arraycopy(buffer, packageLength, remainBytes, 0, remainLength);
                                        decode(remainBytes);
                                    } else if (packageLength > bufferLength) { // 最后一段不完整的消息
                                        LogUtil.w(TAG, "虽然验证全部通过，但出现了一段不完整的消息，包头完整");
                                        nextPackageMinLength = packageLength - bufferLength;
                                        LogUtil.w(TAG, "存进缓冲区，等待下半截，长度至少应该为:" + nextPackageMinLength);
                                        recvBuffer.clear();
                                        // 放进等待区域
                                        recvBuffer.append(buffer, 0, bufferLength);
                                    }
                                } else {
                                    LogUtil.w(TAG, "解析包的返回码不符合协议");
                                }
                            } else {
                                LogUtil.w(TAG, "解析包的流水号不在等待队列");
                            }
//                        } else {
//                            LogUtil.w(TAG, "解析包的协议版本不在约定范围内");
//                        }
                    } else {
                        LogUtil.w(TAG, "解析包命令异常");
                    }
                } else {
                    LogUtil.w(TAG, "解析包长度异常");
                }
            } else {
                LogUtil.e(TAG, "服务端发的包长度不符合协议");
            }
        }

        private String decodeJsonBytes(int packageLength, byte[] buffer) {
            String jsonStr = null;
            if (packageLength > REC_PACKAGE_MIN_LENGTH) {
                try {
                    jsonStr = new String(buffer, REC_PACKAGE_MIN_LENGTH, packageLength - REC_PACKAGE_MIN_LENGTH,
                            "UTF-8");
                } catch (Exception e) {
                    LogUtil.e(TAG, "出现严重的问题，解码byte -> String");
                }
            }
            return jsonStr;
        }

    }
    
    private void initTacticsConnectedSocket() {
        LogUtil.v(TAG, "开始循环连接socket");
        for (int i = 1; i < 20; i++) {
            LogUtil.v(TAG, "第" + i + "循环");
            initSocket();
            if (isNetWorkConnected) {
                if (null != mSynSocket && !mSynSocket.isConnected() && !mSynSocket.isClosed()) {
                    try {
                        mSynSocket.connect(new InetSocketAddress(IP, PORT));
                    } catch (Exception e) {
                        LogUtil.w(TAG, "开始socket连接有异常", e);
                        continue;
                    }
                }
                if (null != mSynSocket && mSynSocket.isConnected() && !mSynSocket.isClosed()) {
                    LogUtil.v(TAG, "socket连接成功");
                    try {
                        if (!isRealConnected) {
                            inputStream = mSynSocket.getInputStream();
                            outputStream = mSynSocket.getOutputStream();
                            isRealConnected = true;
                        }
                        return;
                    } catch (Exception e) {
                        LogUtil.w(TAG, "连接socket获取输出流、输入流异常", e);
                        continue;
                    }
                }
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }
        isRealConnected = false;
    }

    private void initSocket() {
        LogUtil.v(TAG, "初始化socket");
        if (mSynSocket == null || mSynSocket.isClosed()) {
            try {
                mSynSocket = new Socket();
                mSynSocket.setKeepAlive(true);
                mSynSocket.setTcpNoDelay(true);
            } catch (Exception e) {
                LogUtil.e(TAG, "初始化socket有异常", e);
            }
        }
    }
    
    private void closeSocket() {
        if (!isGettingConnect) {
            isRealConnected = false;
            synchronized (lock) {
                if (!isRealConnected) {
                    if (mSynSocket != null) {
                        GeePrefrence.setMySessionId(getApplicationContext(), 0);
                        if (AndroidUtil.isSessionTimeOut(IMService.this)) {
                            if (loginReCmd == null) {
                                loginReCmd = new RECmd(this);
                                loginReCmd.initReloginData(this);
                            }
                            mAryMsgSend.put(loginReCmd.sequenceID, loginReCmd);
                        }
                        try {
                            isRealConnected = false;
                            mSynSocket.close();
                            mSynSocket = null;
                            LogUtil.i(TAG, "关闭了socket");
                        } catch (Exception e) {
                            LogUtil.e(TAG, "关闭socket异常", e);
                        }
                    }

                }
            }
        }
    }
    
    @Override
    public void onStartService() {

        TAG = "IMService";
        LogUtil.v(TAG, "onStartService");

        recvBuffer = new ByteArrayBuffer(REC_PACKAGE_MAX_LENGTH);

        // 初始化网络连接状态
        isNetWorkConnected = AndroidUtil.isNetworkAvailable(this);

        // System.setProperty("java.net.preferIPv6Addresses", "false");

        random = new Random();
        mAryMsgSend = new ConcurrentHashMap<Integer, RECmd>();
        // 重新启动该服务需要检查是否有聊天消息未发送成功，有则自动放进队列
        // 代码等待补上...

        mSendThread = new SendThread();
        mRecvThread = new RecvThread();
        
        if(GeePrefrence.getIsRealRegister(getApplicationContext())){
            mSendThread.start();
            mRecvThread.start();
        }

        serviceReceiver = new ServiceReceiver();

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
        registerReceiver(serviceReceiver, intentFilter);
        
        //升级或重新安装的时候，service重启需要发送重登包
        if(GeePrefrence.getMyUid(getApplicationContext()) > 0){
            GeePrefrence.setMySessionId(getApplicationContext(), 0);
            if (loginReCmd == null) {
                loginReCmd = new RECmd(this);
                loginReCmd.initReloginData(this);
            }
            mAryMsgSend.put(loginReCmd.sequenceID, loginReCmd);
        }
    }

    private ServiceReceiver serviceReceiver;

    private class ServiceReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(CommonUtil.isEmpty(action)){
                return;
            }
            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                if (AndroidUtil.isNetworkAvailable(context)) {
                    isNetWorkConnected = true;
                    mRecvThread.interrupt();
                    mSendThread.interrupt();
                }else{
                    isNetWorkConnected = false;
                    closeSocket();
                }
            }
        }
    }

    /**
     * 来自界面的消息
     */
    @Override
    public void onReceiveMessageFromActivity(Message msg) {
        LogUtil.d(TAG, "收到消息来自activity");
        RECmd reCmd = SendMsgHandle.handleSendMsg(this, msg);
        if (reCmd.parseError == RECmd.PARSE_OK) { // 必须整个组装过程没有任何错误才把消息发出去
            
            reCmd.sequenceID = getUniqueSequenceID();
            reCmd.initData();
            
            if (reCmd.parseError == RECmd.PARSE_OK) {
                
                mAryMsgSend.put(reCmd.sequenceID, reCmd);
                
                // 有新消息来了，叫醒线程
                if (isNetWorkConnected) {
                    if(mSendThread.isThreadStarted){
                        mSendThread.interrupt();
                    }else{
                        mSendThread.start();
                    }
                    if(mRecvThread.isThreadStarted){
                        mRecvThread.interrupt();
                    }else{
                        mRecvThread.start();
                    }
                }
                return;
            }
        }
        LogUtil.e(TAG, "组装失败，消息没有发出去");
        Message errorBackMsg = new Message();
        errorBackMsg.obj = reCmd.tag;
        errorBackMsg.what = reCmd.cmd;
        errorBackMsg.arg1 = ReceiveMsgHandle.ERROR_CODE_ERROR;
        sendMsgToClient(errorBackMsg);
    }

    /**
     * 发送消息到界面
     */
    private void sendMsgToActivity(int cmd, int protocolVersion, int sequenceID, long returnCode, String jsonStr) {
        if(cmd == ProtocolCommand.CMD_HEART_BEAT){
            LogUtil.v(TAG, "心跳回应了");
        }else if(cmd == ProtocolCommand.CMD_REPORT_LOCATION){
            LogUtil.v(TAG, "上报Loc回应了");
        }else{
            RECmd sendRec = mAryMsgSend.remove(sequenceID);
            Message msgToClient = ReceiveMsgHandle.handleReveiveMsg(this, cmd, protocolVersion, returnCode, jsonStr, sendRec, mAryMsgSend);
            if (null != msgToClient) {
                if (msgToClient.arg1 == ReceiveMsgHandle.ERROR_CODE_OK) {
                    LogUtil.i(TAG, "整个消息解码过程正常，准备发送消息给activity");
                } else {
                    LogUtil.e(TAG, "处理服务端返回的消息不正常，马上找原因");
                }
                if(null != sendRec){
                    if(sendRec.isNeedNotifyUI){
                        sendMsgToClient(msgToClient);
                    }else{
                        LogUtil.v(TAG, "该消息不需要通知UI");
                    }
                }else{
                    LogUtil.e(TAG, "处理返回 - 发送recmd is null");
                }
            }else{
                LogUtil.e(TAG, "处理返回 - 发送msgToClient is null");
            }
        }
    }
    
    /**
     * 容器里的reCmd的SequenceID永远保持唯一
     */
    private int getUniqueSequenceID() {
        ramdomInteger = random.nextInt(RECmd.MAX_SEQUENCE_ID);
        if (mAryMsgSend.containsKey(ramdomInteger)) {
            return getUniqueSequenceID();
        } else {
            LogUtil.d(TAG, "生成流水号:" + ramdomInteger);
            if (ramdomInteger < RECmd.HEART_BEAT_SEQUENCE_ID) {
                return ramdomInteger;
            } else {
                LogUtil.e(TAG, "逻辑错误，流水号不能大于心跳流水号");
                return getUniqueSequenceID();
            }
        }
    }
    
    @Override
    public void onStopService() {
        if (null != serviceReceiver) {
            unregisterReceiver(serviceReceiver);
        }
        LogUtil.v(TAG, "onStopService");
    }

}
